using System;

namespace DesignByContract
{

    /// <summary>
    /// Design by contract utility; used to check object contract adherence by client objects
    /// </summary>
    public static class DBC
    {

        //Not worried about differentiating between pre/post conditions and invariants here

        public static void AssertNotNull(object pObject, string pMessage, params string[] pArguments)
        {
            AssertNotNull(pObject, String.Format(pMessage, pArguments));
        }
        /// <summary>
        /// Assert that the object is not null; any type
        /// </summary>
        /// <param name="pObject"></param>
        /// <param name="pMessage"></param>
        /// <exception cref="DesignByContract.AssertionFailureException">If the assertion condition would be false</exception> 
        public static void AssertNotNull(object pObject, string pMessage)
        {
            Assert(pObject != null, pMessage);
        }

        /// <summary>
        /// Assert that the object is not null; use with string types only
        /// </summary>
        /// <param name="pString"></param>
        /// <param name="pMessage"></param>
        /// <exception cref="DesignByContract.AssertionFailureException">If the assertion condition would be false</exception> 
        public static void AssertNotNullOrEmpty(string pString, string pMessage)
        {
            Assert(pString != null && pString.Trim() != String.Empty, pMessage);
        }

        /// <summary>
        /// Assert that the condition is true
        /// </summary>
        /// <param name="pCondition"></param>
        /// <param name="pMessage"></param>
        /// <exception cref="DesignByContract.AssertionFailureException">If the assertion condition would be false</exception> 
        public static void Assert(bool pCondition, string pMessage)
        {
            if (!pCondition)
                throw new AssertionFailureException(pMessage);
            //Debug.Assert(pCondition, pMessage);
        }

        public static void Assert(bool pCondition, string pMessage, params string[] pArguments)
        {
            Assert(pCondition, String.Format(pMessage, pArguments));
        }

		/// <summary>
		/// Assert that a sub class must implement a certain message
		/// </summary>
		/// <param name="pMessage"></param>
		public static void AssertSubTypeResponsibility(string pMessage) {
			throw new NotImplementedException(pMessage);
		}

		/// <summary>
		/// Assert that a sub class must implement a certain message; use this form to supply a never returned
		/// return value in classes that cannot, or should not, be marked abstract
		/// </summary>
		/// <param name="pMessage"></param>
		public static T AssertSubTypeResponsibility<T>(string pMessage) {
			throw new NotImplementedException(pMessage);
		}
    }

    /// <summary>
    /// Thrown by the DBC class when an assertion fails
    /// </summary>
    [Serializable]
    public class AssertionFailureException : ApplicationException
    {

        public AssertionFailureException()
            : base()
        {
        }

        public AssertionFailureException(string pMessage)
            : base(pMessage)
        {
        }

        public AssertionFailureException(string pMessage, Exception pException)
            : base(pMessage, pException)
        {
        }
    }
}
